use std::fs::{File, OpenOptions};
use cores::logger::LogConfig;

use crate::configs::{rbac::RBACConfig};

#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize, Clone, Getters, Setters)]
#[getset(get_mut = "pub", get = "pub", set = "pub")]
pub struct NacosConfig {
    nacos_flag: bool,
    nacos_server: String,
    application_name: String,
}

#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize, Clone, Getters, Setters)]
#[getset(get_mut = "pub", get = "pub", set = "pub")]
pub struct Wxapp {
    appid: String,
    secret: String,
}

//add value to config
impl NacosConfig {
    pub fn validate(&self) {
        if self.nacos_flag {
            if self.nacos_server.is_empty() {
                panic!("请配置nacos_server ！！！！！！！！！！！！！！！！！！！")
            }
            if self.application_name.is_empty() {
                panic!("请配置application_name ！！！！！！！！！！！！！！！！！！！")
            }
        }
    }
}

#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize, Clone, Getters, Setters)]
#[getset(get_mut = "pub", get = "pub", set = "pub")]
pub struct ServerConfig {
    ///当前服务地址
    host: String,
    port: String,
}

#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize, Clone, Getters, Setters)]
#[getset(get_mut = "pub", get = "pub", set = "pub")]
pub struct TenantConfig {
    enable: bool,
    column: String,
    ignore_table: Vec<String>,
}

#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize, Clone, Getters, Setters)]
#[getset(get_mut = "pub", get = "pub", set = "pub")]
pub struct OSSConfig {
    key_id: String,
    key_secret: String,
    endpoint: String,
    bucket: String,
    access_endpoint: String,
}

impl OSSConfig {
    pub fn validate(&self) {
        if self.key_id.is_empty()
            || self.key_secret.is_empty()
            || self.endpoint.is_empty()
            || self.bucket.is_empty()
        {
            panic!("请配置oss ！！！！！！！！！！！！！！！！！！！")
        }
    }
}

///服务启动配置
#[derive(
Debug, PartialEq, serde::Serialize, serde::Deserialize, Clone, Getters, Setters, MutGetters,
)]
#[getset(get_mut = "pub", get = "pub", set = "pub")]
pub struct ApplicationConfig {
    debug: bool,
    log_config: LogConfig,
    ///redis地址
    redis_url: String,
    //mongodb_url地址
    mongodb_url: String,
    /// 数据库地址
    database_url: String,
    /// RBAC权限配置
    rbac_config: RBACConfig,
    ///短信缓存队列（mem/redis）
    sms_cache_send_key_prefix: String,
    ///jwt 秘钥
    secret: String,
    admin_auth_list_api: Option<Vec<String>>,
    super_admin_ids: Vec<String>,
    ///权限缓存类型
    cache_type: String,
    upload_type: String,
    ///重试
    login_fail_retry: u64,
    ///重试等待时间
    login_fail_retry_wait_sec: u64,
    //server 配置
    server: ServerConfig,
    //nacos 配置
    nacos: NacosConfig,
    //租户 配置
    tenant: TenantConfig,
    //oss 配置
    oss: OSSConfig,
    wxapp: Wxapp,
}

fn get_config_file() -> File {
    let config_file_name = "application.yml".to_string();
    OpenOptions::new()
        .create(true)
        .write(true)
        .read(true)
        .open(config_file_name)
        .unwrap()
}

impl Default for ApplicationConfig {
    fn default() -> Self {
        use rand::Rng;
        const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
                                 abcdefghijklmnopqrstuvwxyz\
                                 0123456789)(*&^%$#@!~";
        const PASSWORD_LEN: usize = 32;
        let mut rng = rand::thread_rng();
        Self {
            debug: true,
            redis_url: "redis://127.0.0.1:6379".to_string(),
            database_url: "mysql://root@127.0.0.1:3306".to_string(),
            rbac_config: RBACConfig::default(),
            sms_cache_send_key_prefix: "mem".to_string(),
            // 随机生成密文
            secret: (0..PASSWORD_LEN)
                .map(|_| {
                    let idx = rng.gen_range(0..CHARSET.len());
                    CHARSET[idx] as char
                })
                .collect(),
            admin_auth_list_api: Some(vec![]),
            super_admin_ids: vec![],
            cache_type: "mem".to_string(),
            upload_type: "local".to_string(),
            login_fail_retry: 5,
            login_fail_retry_wait_sec: 10 * 60,
            server: ServerConfig {
                host: "0.0.0,0".to_string(),
                port: "8000".to_string(),
            },
            nacos: NacosConfig {
                nacos_flag: false,
                nacos_server: "127.0.0.1".to_string(),
                application_name: "app_name".to_string(),
            },
            tenant: TenantConfig {
                enable: false,
                column: "tenant".to_string(),
                ignore_table: vec![],
            },
            oss: OSSConfig {
                key_id: "key_id".to_string(),
                key_secret: "key_secret".to_string(),
                endpoint: "endpoint".to_string(),
                bucket: "bucket".to_string(),
                access_endpoint: "access_endpoint".to_string(),
            },
            wxapp: Wxapp {
                appid: "appid".to_string(),
                secret: "secret".to_string(),
            },
            mongodb_url: "mongodb_url".to_string(),
            log_config: LogConfig::default(),
        }
    }
}

impl ApplicationConfig {
    pub fn new() -> Self {
        let config_file = get_config_file();
        match serde_yaml::from_reader(config_file) {
            Ok(config) => config,
            Err(err) => {
                println!("{:#?}", err);
                let config = Self::default();
                config.save();
                panic!("config file <application.yml> created! please edit it and rerun program!");
                // config
            }
        }
    }

    pub fn save(&self) {
        let writer = get_config_file();
        serde_yaml::to_writer(writer, &self).unwrap();
        println!("config file <application.yml> saved success!");
    }
}

#[cfg(test)]
mod tests {
    use super::ApplicationConfig;

    #[test]
    fn it_works() {
        let config = ApplicationConfig::new();
        print!("config save");
        config.save();
    }
}
